package fem2.element;

import inf.jlinalg.Array1DMatrix;
import inf.jlinalg.Array2DMatrix;
import inf.jlinalg.IMatrix;
import inf.jlinalg.MatrixFormat;

import java.lang.reflect.Field;

import math2.MathUtilities;

import org.junit.Assert;
import org.junit.Test;

import fem2.Element;
import fem2.Face;
import fem2.MaterialModel;
import fem2.MeshPart;
import fem2.Node;
import fem2.element.NonlinearStructuralElement;
import fem2.enu.State;
import fem2.material.StVenantKirchhoffMM;

public class NonlinearStructuralElementTest {

	public static Object getFieldValue(String fieldName, Object o)
			throws Exception {
		Field f = o.getClass().getDeclaredField(fieldName);
		return f.get(o);
	}

	public static void setUHatLocal(MeshPart mp, double... uHatLocal)
			throws Exception {
		Field f1 = MeshPart.class.getDeclaredField("uHatLocal");
		f1.setAccessible(true);
		f1.set(mp, uHatLocal);

		mp.updateSolution();
	}

	public Element createElement() {
		Node n1 = new Node(0, 0);
		Node n2 = new Node(3.5, 1);
		Node n3 = new Node(2.3, 2);
		Node n4 = new Node(-1.8, 0.5);

		Face f = new Face(n1, n2, n3, n4);

		n1.setNumberOfFields(2);
		n2.setNumberOfFields(2);
		n3.setNumberOfFields(2);
		n4.setNumberOfFields(2);
		f.setNumberOfFields(2);

		double E = 1000.0;
		double nu = 0.3;
		double t = 1.0;
		MaterialModel mm = new StVenantKirchhoffMM(E, nu, t, 0,
				State.PLANE_STRAIN);

		Element e = new NonlinearStructuralElement(f, mm);
		return e;
	}

	@Test
	public void testGradient() {
		Element e = createElement();
		Face f = (Face) e.getMeshPart();
		double[][] g = f.gGradientsAt(0.2, 0.4);

		double[][] expected_g = new double[][] {
				{ 5.905512E-02, -3.937008E-01 },
				{ 2.066929E-01, -3.779528E-01 },
				{ -4.92126E-02, 6.614173E-01 }, { -2.165354E-01, 1.102362E-01 } };

		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 2; j++) {
				Assert.assertEquals(expected_g[i][j], g[i][j], MathUtilities.tol6);
			}
		}
	}

	@Test
	public void testJacobian() throws Exception {
		Element e = createElement();
		Face f = (Face) e.getMeshPart();

		double[][] jac = f.jacXAt(0.2, 0.4);

		double[][] expected_jac = new double[][] { { 1.96000E00, 6.75000E-01 },
				{ -7.20000E-01, 4.00000E-01 } };

		for (int i = 0; i < 2; i++) {
			for (int j = 0; j < 2; j++) {
				Assert.assertEquals(expected_jac[i][j], jac[i][j],
						MathUtilities.tol14);
			}
		}
	}

	@Test
	public void testJacU() throws Exception {
		Element e = createElement();
		Face f = (Face) e.getMeshPart();
		setUHatLocal(f, 1, 5, 4, 3, 2, 5, 8, 2);

		double[][] ju = f.jacUAt(0.2, 0.4);
		// System.out.println(ArrayFormat.format(ju));

		double[][] expected_ju = new double[][] { { -1.65000E00, 7.50000E-01 },
				{ 8.00000E-01, 1.110223E-16 } };

		for (int i = 0; i < 2; i++) {
			for (int j = 0; j < 2; j++) {
				Assert.assertEquals(expected_ju[i][j], ju[i][j], MathUtilities.tol14);
			}
		}

		// double[] g1 = f.getSolution(0).gradientAt(0.2, 0.4);
		// System.out.println("g1 = " + ArrayFormat.format(g1));
		// double[] g2 = f.getSolution(1).gradientAt(0.2, 0.4);
		// System.out.println("g2 = " + ArrayFormat.format(g2));

		// LinearCombinationRnToR sol1 = (LinearCombinationRnToR)
		// f.getSolution(0);
		// double[] coeff = (double[]) getFieldValue("coeffs", sol1);
		// double[] coeff = sol1.getCoefficients();
		// System.out.println(ArrayFormat.format(coeff));

		// LinearCombinationRnToR sol2 = (LinearCombinationRnToR)
		// f.getSolution(1);
		// double[] coeff = (double[]) getFieldValue("coeffs", sol1);
		// coeff = sol2.getCoefficients();
		// System.out.println(ArrayFormat.format(coeff));
	}

	@Test
	public void testGJacU() throws Exception {
		Element e = createElement();
		Face f = (Face) e.getMeshPart();
		setUHatLocal(f, 1, 5, 4, 3, 2, 5, 8, 2);

		double[][] gju = f.gJacUAt(0.2, 0.4);

		double[][] expected_gju = new double[][] {
				{ -9.448819E-01, 2.362205E-01 }, { 2.992126E-01, 4.251969E-01 } };

		for (int i = 0; i < 2; i++) {
			for (int j = 0; j < 2; j++) {
				Assert.assertEquals(expected_gju[i][j], gju[i][j],
						MathUtilities.tol6);
			}
		}
	}

	@Test
	public void testBoperator() throws Exception {
		Element e = createElement();
		MeshPart mp = e.getMeshPart();

		setUHatLocal(mp, 1, 5, 4, 3, 2, 5, 8, 2);

		int n = mp.getBasis().getDimension();
		int dim = mp.getDimension();
		Array1DMatrix B = Array1DMatrix
				.create(Array1DMatrix.ORDERING_COLUMN_MAJOR);
		B.setSize(dim * (dim + 1) / 2, dim * n);

		((NonlinearStructuralElement) e).computeB(mp, B, 0.2, 0.4);

		double[][] expectedB = {
				{ 0.003255006510013026, 0.013950027900055792,
						0.011392522785045593, 0.04882509765019527,
						-0.002712505425010854, -0.011625023250046487,
						-0.011935023870047764, -0.05115010230020457 },
				{ -0.11780023560047126, -0.5611011222022445,
						-0.11308822617645238, -0.5386570773141546,
						0.19790439580879168, 0.9426498852997706,
						0.03298406596813194, 0.1571083142166284 },
				{ -0.004030008060016188, -0.008835017670035333,
						0.041013082026163994, 0.2052979105958212,
						0.021731043462086985, 0.0861026722053444,
						-0.05871411742823486, -0.2825655651311303 } };

		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 8; j++) {
				Assert.assertEquals(expectedB[i][j], B.get(i, j), MathUtilities.tol6);
			}
		}
	}

	@Test
	public void testKge() throws Exception {
		Element e = createElement();
		MeshPart mp = e.getMeshPart();

		setUHatLocal(mp, 1, 5, 4, 3, 2, 5, 8, 2);

		int n = mp.getBasis().getDimension();
		int dim = mp.getDimension();
		IMatrix kge = new Array2DMatrix(dim * n, dim * n);

		e.Initialize();
		e.InitializeSolutionStep();
		((NonlinearStructuralElement) e).computeKge(kge);
		System.out.println(MatrixFormat.format(kge));

		double[][] expectedKg = {
				{ 22498.38723906554, 0., -978.165776449657, 0.,
						-7911.549467962766, 0., -13608.67199465312, 0. },
				{ 0., 22498.38723906554, 0., -978.165776449657, 0.,
						-7911.549467962766, 0., -13608.67199465312 },
				{ -978.165776449658, 0., 2157.140093413893, 0.,
						-1819.319853034811, 0., 640.3455360705766, 0. },
				{ 0., -978.165776449658, 0., 2157.140093413893, 0.,
						-1819.319853034811, 0., 640.3455360705766 },
				{ -7911.549467962767, 0., -1819.319853034812, 0.,
						5528.350526764338, 0., 4202.51879423324, 0. },
				{ 0., -7911.549467962767, 0., -1819.319853034812, 0.,
						5528.350526764338, 0., 4202.51879423324 },
				{ -13608.67199465312, 0., 640.3455360705766, 0.,
						4202.51879423324, 0., 8765.80766434931, 0. },
				{ 0., -13608.67199465312, 0., 640.3455360705766, 0.,
						4202.51879423324, 0., 8765.80766434931 } };

		for (int i = 0; i < 8; i++) {
			for (int j = 0; j < 8; j++) {
				Assert.assertEquals(expectedKg[i][j], kge.get(i, j),
						MathUtilities.tol6);
			}
		}
	}
}
